package com.ljf.common.infra.repository.impl;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ljf.common.domain.repository.IBasisRepository;
import com.ljf.common.dto.query.PageParam;
import com.ljf.common.enums.StateEnum;
import com.ljf.common.exception.ServiceException;
import com.ljf.common.infra.mapper.BasisMapper;
import com.ljf.common.model.BasisModel;
import com.ljf.common.utils.BeanConversionUtil;
import com.ljf.common.utils.CollectionUtil;
import com.ljf.common.utils.WrapperUtil;
import com.ljf.config.annotation.AutoCode;
import com.ljf.config.domain.service.ConfigBaseImpulseSenderHeaderDomainService;
import org.springframework.context.ApplicationContext;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 基础数据访问接口定义
 *
 * @author LJF
 * @since 2024-01-26
 */
public class BasisRepositoryImpl<P extends BasisMapper<M>, M extends BasisModel, Q extends PageParam, V extends Serializable> implements IBasisRepository<Q, V, M> {

    private static final Integer MAX_BATCH_SELECT = 500;
    private static final String ID_COLUMN_NAME = "id";

    @Resource
    private ApplicationContext applicationContext;

    protected P mapper;

    private Class<P> mapperClass;

    /**
     * 新增数据
     *
     * @param model
     * @return
     */
    @Override
    public M insert(M model) throws ServiceException {
        List<M> modelList = new ArrayList<>();
        modelList.add(model);
        this.fillInsertInfo(modelList);
        mapper.insert(model);
        return model;
    }

    /**
     * 批量新增数据
     *
     * @param modelList
     * @return
     */
    @Override
    public List<M> insertBatch(List<M> modelList) throws ServiceException {
        this.fillInsertInfo(modelList);
        for (M model : modelList) {
            mapper.insert(model);
        }
        return modelList;
    }

    /**
     * 修改数据
     *
     * @param model
     * @return
     */
    @Override
    public int updateById(M model) {
        return mapper.updateById(model);
    }

    /**
     * 修改数据
     *
     * @param model
     * @param query
     * @return
     * @throws ServiceException
     */
    @Override
    public int update(M model, Q query) throws ServiceException {
        return mapper.update(model, fillWrapper(query, false));
    }

    /**
     * 批量修改数据
     *
     * @param modelList
     * @return
     * @throws ServiceException
     */
    @Override
    public int updateBatchByIds(List<M> modelList) throws ServiceException {
        int result = 0;
        for (M model : modelList) {
            result += updateById(model);
        }
        return result;
    }

    /**
     * 分页查询
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    @Override
    public Page<V> selectPageList(Q query) throws ServiceException {
        Page<M> page = new Page<>(query.getCurrent(), query.getSize());
        IPage<M> selectPage = mapper.selectPage(page, fillWrapper(query, true));
        Page<V> resultPage = new Page<>(query.getCurrent(), query.getSize());
        List<V> voList = BeanConversionUtil.conversionList(selectPage.getRecords(), (Class<V>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[3]);
        fillInfo(voList);
        resultPage.setRecords(voList);
        resultPage.setTotal(page.getTotal());
        return resultPage;
    }

    /**
     * 列表查询
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    @Override
    public List<V> selectList(Q query) throws ServiceException {
        QueryWrapper<M> wrapper = fillWrapper(query, true);
        if (mapper.selectCount(wrapper) > MAX_BATCH_SELECT) {
            throw new ServiceException("通用列表查询异常:单次查询数量过多");
        }
        List<M> selectList = mapper.selectList(wrapper);
        List<V> resultList = BeanConversionUtil.conversionList(selectList, (Class<V>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[3]);
        fillInfo(resultList);
        return resultList;
    }

    /**
     * 根据ID的列表查询
     *
     * @param ids
     * @return
     * @throws ServiceException
     */
    @Override
    public List<V> selectListByIds(List<Long> ids) throws ServiceException {
        if (CollectionUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<M> selectList = mapper.selectList(new QueryWrapper<M>().in(ID_COLUMN_NAME, ids).eq(BasisModel.COL_STATE, StateEnum.ENABLE.getCode()));
        List<V> resultList = BeanConversionUtil.conversionList(selectList, (Class<V>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[3]);
        fillInfo(resultList);
        return resultList;
    }

    /**
     * 根据ID查询
     *
     * @param id
     * @return
     * @throws ServiceException
     */
    @Override
    public V selectById(Long id) throws ServiceException {
        M model = mapper.selectById(id);
        if (model == null) {
            return null;
        }
        List<V> resultList = new ArrayList<>();
        resultList.add(BeanConversionUtil.conversionObj(model, (Class<V>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[3]));
        fillInfo(resultList);
        return resultList.get(0);
    }

    /**
     * 查询记录数
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    @Override
    public int count(Q query) throws ServiceException {
        return mapper.selectCount(fillWrapper(query, false));
    }

    /**
     * 根据ID查询记录数
     *
     * @param ids
     * @return
     * @throws ServiceException
     */
    @Override
    public int countByIds(List<Long> ids) throws ServiceException {
        if (CollectionUtil.isEmpty(ids)) {
            return 0;
        }
        return mapper.selectCount(
                new QueryWrapper<M>().in(ID_COLUMN_NAME, ids)
        );
    }

    /**
     * 根据ID查询记录数
     *
     * @param id
     * @return
     * @throws ServiceException
     */
    @Override
    public int countById(Long id) throws ServiceException {
        if (id == null) {
            return 0;
        }
        return mapper.selectCount(new QueryWrapper<M>().eq(ID_COLUMN_NAME, id));
    }

    /**
     * 根据条件查询唯一记录
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    @Override
    public V selectOne(Q query) throws ServiceException {
        return selectOne(query, "通用唯一查询异常:记录不存在", "通用唯一查询异常:记录存在多条");
    }

    /**
     * 根据条件查询唯一记录
     *
     * @param query
     * @param noRecord
     * @param manyRecord
     * @return
     * @throws ServiceException
     */
    @Override
    public V selectOne(Q query, String noRecord, String manyRecord) throws ServiceException {
        int count = count(query);
        if (count == 0) {
            throw new ServiceException(noRecord);
        }
        else if (count > 1) {
            throw new ServiceException(manyRecord);
        }
        return selectList(query).get(0);
    }

    @Override
    public int deleteById(Long id) throws ServiceException {
        return mapper.update(generateDisableModel(), new QueryWrapper<M>().eq(ID_COLUMN_NAME, id));
    }

    @Override
    public int delete(Q query) throws ServiceException {
        return mapper.update(generateDisableModel(), fillWrapper(query, false));
    }

    /**
     * 根据ID批量删除
     *
     * @param ids
     * @return
     * @throws ServiceException
     */
    @Override
    public int deleteBatchByIds(List<Long> ids) throws ServiceException {
        if (CollectionUtil.isEmpty(ids)) {
            return 0;
        }
        return mapper.update(generateDisableModel(), new QueryWrapper<M>().in(ID_COLUMN_NAME, ids));
    }

    /**
     * 根据ID批量彻底删除
     *
     * @param ids
     * @return
     * @throws ServiceException
     */
    @Override
    public int physicalDeleteBatchByIds(List<Long> ids) throws ServiceException {
        return mapper.deleteBatchIds(ids);
    }

    /**
     * 根据查询条件彻底删除
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    @Override
    public int physicalDelete(Q query) throws ServiceException {
        return mapper.delete(fillWrapper(query, false));
    }

    /**
     * 填充查询条件
     *
     * @param query
     * @param sort
     * @return
     * @throws ServiceException
     */
    protected QueryWrapper<M> fillWrapper(Q query, boolean sort) throws ServiceException {
        M selectModel = BeanConversionUtil.conversionObj(query, (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]);
        try {
            QueryWrapper<M> condition = WrapperUtil.getCondition(selectModel);
            condition.eq(BasisModel.COL_STATE, StateEnum.ENABLE.getCode());
            if (sort) {
                return condition.orderByDesc(BasisModel.COL_CREATE_TIME);
            }
            return condition;
        } catch (IllegalAccessException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 填充返回信息
     *
     * @param resultList
     * @throws ServiceException
     */
    protected void fillInfo(List<V> resultList) throws ServiceException {

    }

    /**
     * 填充新增信息
     *
     * @param modelList
     * @throws ServiceException
     */
    private void fillInsertInfo(List<M> modelList) throws ServiceException {
        if (CollectionUtil.isEmpty(modelList)) {
            return;
        }
        Field[] fieldList = modelList.get(0).getClass().getDeclaredFields();
        boolean fillAutoCode = false;
        String fillAutoCodeName = "";
        for (Field field : fieldList) {
            AutoCode autoCode = field.getAnnotation(AutoCode.class);
            fillAutoCodeName = field.getName();
            if (autoCode != null) {
                fillAutoCode = true;
                break;
            }
        }
        try {
            if (fillAutoCode) {
                TableName tableName = modelList.get(0).getClass().getAnnotation(TableName.class);
                if (tableName == null) {
                    throw new ServiceException("新增自动填充数据异常:获取表名异常");
                }
                List<String> codeSignList = new ArrayList<>();
                for (int i = 0; i < modelList.size(); i++) {
                    codeSignList.add(tableName.value() + "-" + fillAutoCodeName);
                }
                ConfigBaseImpulseSenderHeaderDomainService senderHeaderService = applicationContext.getBean(ConfigBaseImpulseSenderHeaderDomainService.class);
                List<String> codeList = senderHeaderService.getUniqueCodeList(codeSignList, true);
                for (int i = 0; i < modelList.size(); i++) {
                    Field field = modelList.get(i).getClass().getDeclaredField(fillAutoCodeName);
                    field.setAccessible(true);
                    field.set(modelList.get(i), codeList.get(i));
                }
            }
        }
        catch (NoSuchFieldException e1) {
            throw new ServiceException("新增自动填充数据异常:获取属性名称错误");
        }
        catch (IllegalAccessException e2) {
            throw new ServiceException("新增自动填充数据异常:调用方法错误");
        }
    }

    /**
     * 构造删除Model对象
     *
     * @return
     * @throws ServiceException
     */
    private M generateDisableModel() throws ServiceException {
        M model;
        try {
            model = ((Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance();
        } catch (InstantiationException e) {
            e.getMessage();
            throw new ServiceException("通用删除异常:构造实体失败");
        } catch (IllegalAccessException e) {
            e.getMessage();
            throw new ServiceException("通用删除异常:构造实体失败");
        }
        model.setState(StateEnum.DISABLE.getCode());
        return model;
    }

    @PostConstruct
    private void init() {
        this.getMapper();
    }

    /**
     * 获取服务对象
     *
     * @return
     */
    private P getMapper() {
        if (this.mapper == null) {
            Class<P> sampleObjectCallBack = this.getSuperClassGenricType(0);
            this.mapper = this.applicationContext.getBean(sampleObjectCallBack);
        }
        return this.mapper;
    }

    /**
     * 获取服务Bean类型
     *
     * @param index
     * @return
     */
    private Class<P> getSuperClassGenricType(int index) {
        ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
        int count = type.getActualTypeArguments().length;
        if (count > 0) {
            this.mapperClass = (Class) type.getActualTypeArguments()[index];
        }
        return this.mapperClass;
    }

}
